# coding:utf-8
import os
import sys
import streamlit as st

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
if project_root not in sys.path:
    sys.path.append(project_root)
from pydub import AudioSegment

AudioSegment.converter = r"D:\code\BERT-NER-Pytorch\wanderlust_companion\test\ffmpeg\bin\ffmpeg.exe"
from config.config import Config
from src.get_weather import WeatherAndtime
from src.chat import chat, generate_response
from src.audio_recognition import audio_text
from src.image_understand import generate_text_from_image
from src.rag import load_vector_database, embedding_make
from src.route_plan import get_citycode, geocode_address, get_transit_plan, parse_transit_routes
from llama_index.core.vector_stores import VectorStoreQuery
from st_audiorec import st_audiorec
from io import BytesIO
import json
import time
import requests
from openai import OpenAI
import random

from openai import OpenAI

import requests
from parsel import Selector
from fuzzywuzzy import process
import sqlite3
# 天气及时间
import pytz
import requests
from datetime import datetime

import posthog

posthog.disabled = True


## -----------------------根据目的地，按需推荐景点，-匹配景点，进行规划，爬取图片，显示-------------

# 从本地中匹配目标地的url
def get_url_by_city(city_name, filename='../scene/city_urls.json'):
    with open(filename, 'r', encoding='utf-8') as file:
        city_data = json.load(file)
    if city_name in city_data:
        print(f"{city_name} 的网址是: {city_data[city_name]}")
        return city_data[city_name]
    else:
        print(f"未找到 {city_name} 的链接。")
        return None


url = "https://you.ctrip.com/"
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0'
}


def get_response(url, headers):
    response = requests.get(url=url, headers=headers)
    return response


# 通过城市名获取该地景点url及名称
def get_scene_url(url_per_page):
    response = get_response(url_per_page, headers)
    content = response.text
    select = Selector(content)
    url_scene = select.css('.sightItemCard_box__2FUEj .titleModule_name__Li4Tv a::attr(href)').getall()
    attractions_name = select.css('.sightItemCard_box__2FUEj .titleModule_name__Li4Tv a::text').getall()
    return url_scene, attractions_name


# 获取景点名称及url ，可重试三次
def get_attractions(city_dropdown):
    city_url = get_url_by_city(city_dropdown)
    base_url = city_url.split('.html', 1)[0]
    extension = ".html"
    url_scene = []
    attractions_name = []

    # 爬取的页面范围
    # for i in range(1, 11):
    for i in range(1, 6):

        # 构建新的路径段
        new_segment = f"/s0-p{i}{extension}"

        # 拼接成完整的URL
        modified_url = base_url + new_segment

        # 重试机制
        retries = 3
        for attempt in range(retries):
            try:
                url_scene_tmp, attractions_name_tmp = get_scene_url(modified_url)
                url_scene += url_scene_tmp
                attractions_name += attractions_name_tmp
                break  # 如果成功，跳出重试循环
            except Exception as e:
                if attempt < retries - 1:  # 如果不是最后一次重试，等待并继续尝试
                    print(f"Error occurred: {e}. Retrying ({attempt + 1}/{retries})...")
                    time.sleep(1)  # 等待2秒后重试
                else:
                    print(f"Error occurred: {e}. All retries failed.")
                    continue

    return attractions_name, url_scene


#  对比用户的输入，返回最相似的一个景点名称
def match_name(attractions_name, input_name):
    """results 是一个包含元组的列表，每个元组有两个值：第一个是匹配的字符串，第二个是相似度分数（0 到 100）。
        if results[0][1] > 50:：
        results[0][1] 访问的是第一个匹配项的相似度分数。
        如果第一个匹配的相似度大于 50，则进入 if 语句。"""
    results = process.extract(input_name, attractions_name, limit=3)
    if results[0][1] > 50:
        return results[0][0]
    else:
        return None


# 以换行切分景点名称
def split_name(names):
    attractions = [attraction.strip() for attraction in names.split('\n')]
    return attractions


# 模型按目的地推荐景点
def name_tour(destination, chat_days, chat_style, chat_budget, chat_people):
    client = OpenAI(
        api_key="sk-noqgjivyynnqvswqrbbvjfbmrgxdwvjgcpqqrgfejbsvdxmu",
        base_url="https://api.siliconflow.cn/v1/"
    )
    prompt_1 = """我现在要出去旅游，请根据目的地{}，天数：{}天 ，行程风格：{} ，预算：{}，随行人数：{},推荐几个热门景点，
确保我的时间足够游玩完，可以借鉴行程风格合理推荐个数。如1天给我推荐3个景点，2天给我推荐5个景点，3天给我推7个景点，以此类推。太多就不合理了！
只输出景点的名称（名称尽量和携程旅游一致），不要重复景点,确保全是中文且大众的景点，以热度排序！
输出格式为：
景点名称
景点名称
……
"""
    query1 = prompt_1.format(destination, chat_days, chat_style, chat_budget, chat_people)
    print("推荐景点的query：\n", query1)
    messages = [
        {'role': 'system', 'content': '你是一个旅行助手'},
        {'role': 'user', 'content': query1}
    ]
    response = client.chat.completions.create(
        model="Vendor-A/Qwen/Qwen2.5-72B-Instruct",
        messages=messages,
    )
    return response.choices[0].message.content


#  获取三个景点名称
def get_example(destination, attractions_output, chat_days, chat_style, chat_budget, chat_people):
    names = name_tour(destination, chat_days, chat_style, chat_budget, chat_people)  # 字符串，推荐几个景点
    print("get_example函数的景点名称：\n", names)
    attr = []  # 匹配后的，景点名称列表
    attractions = split_name(names)  # 多个生成的景点，没模糊匹配的

    for i in range(0, len(attractions)):
        attr.append(match_name(attractions_output, attractions[i]))

    cleaned_places = []
    seen = set()
    # 有序去重
    for place in attr:
        if place is not None and place not in seen:
            cleaned_places.append(place)
            seen.add(place)

    print(cleaned_places)
    return cleaned_places


#  给景点url，获取景点的id
def split_scene_id(str):
    parts = str.split('/')
    for part in parts:
        if '.html' in part:
            scene_id = part.split('.')[0].split('r')[1].strip()
            break
    return scene_id


# 通过景点url 获取获取第一张图片的id
def get_phsid(url_img):
    response = get_response(url_img, headers)
    selector = Selector(response.text)
    phsid = selector.css('.photolist .item::attr(phsid)')[0].get()  # 获取第一张图片的id
    return phsid


# 提取url返回列表
def get_large_image_url(scene_id, phsid):
    url_imgs = []
    url = f'https://you.ctrip.com/Destinationsite/TTDSecond/Photo/AjaxPhotoDetailList?districtId=2&type=2&pindex=1&phsid={phsid}&resource={scene_id}'
    response = get_response(url, headers)
    response_dict = response.json()
    url_imgs = [item['bigImgUrl'] for item in response_dict[:6]]
    return url_imgs


# 单个景点信息，可返回景点信息和图片url
def get_info(scene_name, attractions_text, scene_url_text):
    url_scene_dict = {k: v for k, v in zip(attractions_text, scene_url_text)}

    # print(f"get_info函数的url_s,用户的输入城市:{scene_name2}\n",url_scene_dict)
    # scene_name = match_name(attractions_text,scene_name2)
    # print("模糊匹配的景点名称：",scene_name)
    url = url_scene_dict[scene_name]

    response = get_response(url=url, headers=headers)
    print("进入get_response 函数 168行")
    content = response.text
    # 发送请求获得景点数据
    selector = Selector(content)
    # 创建selector对象
    # heat=selector.css('.baseInfoMain .heatView .heatScoreView .heatScoreText::text').get()
    # comment=selector.css('.baseInfoMain .comment .commentScoreNum::text').get()+selector.css('.baseInfoMain .comment .commentScoreText::text').get()
    # commentcount=selector.css('.baseInfoMain .comment .commentCount>span::text').get()
    # commentnum=f"{commentcount}条评论"
    # place=selector.css('.baseInfoMain .baseInfoContent .baseInfoItem .baseInfoText::text').get()
    # open_time=selector.css('.moduleContent::text').get()
    img_url = selector.css('.swiperMain .totalCont>a::attr(href)').get()

    print(f"该景点{scene_name}的图片网页地址：", img_url)
    # public_comment=selector.css('.commentModule .commentList .commentItem .commentDetail')
    # p_comment=[]
    # user_comment='三、游客评论\n\n'
    # for i in range(1,4):
    #     p_comment=f"游客{i}：{public_comment[i].css('::text').get()}\n\n"
    #     user_comment=user_comment+p_comment
    # info=f"二、景点信息\n\n热度：{heat}\n评分：{comment}\n评论数目：{commentnum}\n地址：{place}\n开放时间：{open_time}\n\n{user_comment}"
    scene_id = split_scene_id(img_url)
    # print("进入split_scend_id函数")
    phsid = get_phsid(img_url)
    # print("进入get_phsid函数")
    url_photos = get_large_image_url(scene_id, phsid)
    # print("进入get_large_image_url函数")
    # return info,url_photos
    return url_photos


# 获取多个景点的图片url
def get_image_url(scene_list, attractions_list, attractions_url):
    # 你的代码逻辑
    image_info = {}
    for scene_name in scene_list:
        # 如果该景点已经存在，跳过
        if scene_name not in image_info:
            # 2. 通过景点列表挨个去获取景点的信息（图片url）
            image_url = get_info(scene_name, attractions_list, attractions_url)
            image_info[scene_name] = image_url

    # 打印获取到的景点对应图片信息
    print("获取到的景点对应图片信息：", image_info)
    return image_info


def save2json(image_info, path='../scene/image_info.json'):
    # 保存 image_info 到一个 JSON 文件
    with open(path, 'w', encoding='utf-8') as json_file:
        json.dump(image_info, json_file, ensure_ascii=False, indent=4)

    print(f"信息已保存到 {path}")


from RAG.langchain_chroma_sqlite3_rag import store_chat_history,setup_database

setup_database()
# conn = sqlite3.connect("../RAG/chat_history.db")
conn = sqlite3.connect("./chat_history.db")

def main():
    # 确保 config 只初始化一次
    if "config" not in st.session_state:
        st.session_state.config = Config()  # 只在首次运行时加载模型

    config = st.session_state.config  # 之后直接复用已有的 config
    # config = Config()

    st.title(":blue[Wanderlust_Companion-旅游助手]")

    # 通过侧边栏选择功能页面
    st.sidebar.title("ipex-llm推理部署旅游领域应用")

    assistant_type = st.sidebar.radio(
        "您的专属旅游助手",
        ("旅游规划助手", "旅游陪伴助手", "旅游文案助手")
    )
    mode = st.sidebar.radio(
        "推理模式",
        ("本地推理", "在线推理")
    )
    st.sidebar.image("../img/logo.png")

    # 判断选择的助手类型
    if assistant_type == "旅游规划助手":
        st.header("旅游规划助手")
        st.markdown(
            "欢迎使用旅游规划助手，为您的旅行制定最佳计划，帮助您规划每个细节，包括目的地游玩公交路线规划，开始您的旅行吧！")
        st.markdown("请选择您需要的服务类型：")
        # 选择具体的旅游规划功能
        planning_type = st.radio("请选择功能", ["📌 旅游行程规划", "🚍 公交路线规划", ])
        if planning_type == "🚍 公交路线规划":
            st.subheader("🚍 公交路线规划")
            col1, col2 = st.columns(2)
            with col1:
                origin_address = st.text_input("出发地", "合肥站")
            with col2:
                dest_address = st.text_input("目的地", "合肥骆岗公园")

            city_name = st.text_input("输入目的城市：", "合肥")
            if st.button("开始规划"):
                st.success(f"正在查询从 {origin_address} 到 {dest_address} 的公交路线...")
                city_code = get_citycode(city_name)  # 先获取 citycode
                origin = geocode_address(origin_address)
                destination = geocode_address(dest_address)

                if origin and destination:
                    # 获取公交路线规划数据
                    plan_data = get_transit_plan(origin, destination, city_code)
                    if plan_data and plan_data.get("status") == "1":
                        # 解析公交路线并以表格形式展示
                        route_data = plan_data['route']['transits']
                        print(route_data)
                        transit_routes = parse_transit_routes(route_data)

                        if transit_routes:
                            st.markdown("### 🚍 推荐公交路线")
                            st.table(transit_routes)
                        else:
                            st.warning("未找到合适的公交路线")
                    else:
                        st.error("没有找到合适的公交路线")
                else:
                    st.error("无法解析地址，请检查输入")

        elif planning_type == "📌 旅游行程规划":

            # 使用 `st.columns()` 将输入框分布到两列或三列
            col1, col2, col3 = st.columns(3)

            with col1:
                chat_departure = st.text_input("出发地", value="合肥")
                chat_budget = st.number_input("预算 (元)", min_value=500, max_value=100000, value=3000)

            with col2:
                chat_destination = st.text_input("目的地", value="南京")
                chat_people = st.number_input("随行人数", min_value=1, max_value=10, value=1)

            with col3:
                chat_days = st.number_input("天数", min_value=1, max_value=30, value=2)
                chat_style = st.selectbox("行程风格", ["紧凑", "适中", "休闲"])

            with st.expander("额外需求（可选）"):
                chat_other = st.text_input("特殊要求", value="无")

            if st.button("生成旅行计划表"):
                st.success("旅行计划表生成中...")

                # 获取目的地的地址
                current_weather = WeatherAndtime(config.weather_key).get_weather(chat_destination)

                # 添加 推荐的景点
                attractions_list, attractions_url = get_attractions(chat_destination)  # 获取景点名称及url 列表

                # 按照天数及人数推荐景点并匹配上图所存在的数据
                scene_list = get_example(chat_destination, attractions_list, chat_days, chat_style, chat_budget,
                                         chat_people)  # 返回推荐的景点的列表

                response = chat(config, chat_destination, chat_departure, chat_days, chat_style, chat_budget,
                                chat_people,
                                chat_other, current_weather, mode, scene_list)
                if response:
                    # 使用 DataFrame 显示表格
                    # df = parse_plan_to_table(response)
                    # st.table(df)

                    st.markdown(response)
                    # st.table(response)
                with st.spinner('Wait for it...'):
                    image_info = get_image_url(scene_list, attractions_list, attractions_url)

                    save2json(image_info, path='../scene/image_info.json')
                    # 从 JSON 文件中读取数据
                    with open('../scene/image_info.json', 'r', encoding='utf-8') as f:
                        image_data = json.load(f)

                    # 使用 Streamlit 显示图像
                    for place, image_urls in image_data.items():
                        st.subheader(place)  # 使用景点名称作为标题

                        # 创建 3 列，用于显示图片，每行最多 3 张图像
                        cols = st.columns(3)

                        # 遍历图像链接列表并按列显示图像
                        for i, img_url in enumerate(image_urls):
                            col = cols[i % 3]  # 轮流使用三列
                            col.image(img_url, use_container_width=True)  # 显示景点名称作为图像标题

                # st.success('Done!')

    # 页面2: 旅游陪伴助手
    elif assistant_type == "旅游陪伴助手":
        st.header("旅游陪伴助手")
        st.markdown("与您的旅行伙伴一起探索这个世界，让我们为您提供最佳旅行伴侣和建议。")

        is_arg = st.radio("选择问答模式", ("简单模式", "知识库模式", "语音模式"))
        rag_type = st.radio("rag处理模式（针对知识库模式）", ["一次性全部加载", "针对性加载"])
        if "messages" not in st.session_state:
            st.session_state.messages = []

        # 显示聊天历史
        for message in st.session_state.messages:
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

        prev_is_arg = st.session_state.get("prev_is_arg", None)  # 记录上一次的模式

        # 退出语音模式时释放 Whisper 相关模型
        if prev_is_arg == "语音模式" and is_arg != "语音模式":

            st.session_state.prev_is_arg = is_arg  # 更新当前模式
            if "whisper_model" in st.session_state:
                st.success(f"语音模型已清理")
                del st.session_state.whisper_model
            if "whisper_processor" in st.session_state:
                del st.session_state.whisper_processor

        if prompt := st.chat_input("你可以询问关于旅游攻略问题"):

            if is_arg == "知识库模式":

                if rag_type == "一次性全部加载":
                    st.success(f"数据库加载中")
                    vector_store = load_vector_database(config, persist_dir=config.persist_dir,
                                                        data_path=config.data_path)
                    st.success(f"数据库已加载")

                    query_embedding = config.embed_model.get_query_embedding(prompt)
                    query_mode = "default"

                    vector_store_query = VectorStoreQuery(
                        query_embedding=query_embedding,
                        similarity_top_k=3,
                        mode=query_mode,
                        filters=None  # 显式设置 filters=None
                    )

                    # 打印数据库状态
                    print(f"Query Mode: {query_mode}")
                    # print(f"数据库当前文档数量: {vector_store.chroma_collection.count()}")

                    query_result = vector_store.query(vector_store_query)
                    print(f"检索到：{query_result}")
                    texts = " ".join([node.text for node in query_result.nodes])
                    print(texts)
                    final_prompt = f"你是一个旅游助手,你的任务是根据已收集到的信息：\n{texts}.\n来简要回答用户所提出的问题：{prompt}。"
                    real_prompt = final_prompt

                elif rag_type == "针对性加载":
                    real_prompt = embedding_make(prompt)

            if is_arg == "简单模式":
                real_prompt = prompt
            st.session_state.messages.append({"role": "user", "content": prompt})
            with st.chat_message("user"):
                st.markdown(prompt)

            response = ""
            with st.chat_message("assistant"):
                message_placeholder = st.empty()

                streamer = generate_response(real_prompt, config, mode)
                for text in streamer:
                    response += text
                    message_placeholder.markdown(response + "▌")

                message_placeholder.markdown(response)
            st.session_state.messages.append({"role": "assistant", "content": response})

            store_chat_history(conn, prompt, response)

        if is_arg == "语音模式":
            wav_audio_data = st.audio_input("Record a voice message")
            if wav_audio_data is not None:
                # Get the byte data from the UploadedFile object
                wav_audio_data_bytes = wav_audio_data.getvalue()  # This is the key part
                # Now, you can pass the byte data to BytesIO
                with BytesIO(wav_audio_data_bytes) as audio_bytes:
                    audio_segment = AudioSegment.from_file(audio_bytes, format="wav")
                # Set audio properties
                audio_segment = audio_segment.set_frame_rate(16000).set_sample_width(2).set_channels(1)
                TEMP_AUDIO_DIR = r"D:\code\BERT-NER-Pytorch\wanderlust_companion\web"
                unique_filename = 'audio' + ".mp3"
                temp_filepath = os.path.join(TEMP_AUDIO_DIR, unique_filename)
                # Export to MP3
                audio_segment.export(temp_filepath, format="mp3")
                # st.success(f"录音已保存为 {temp_filepath}")
                # 加载模型，语音转文本
                prompt = audio_text(temp_filepath, config, mode)
                print(prompt)

                # 展示识别结果
                st.session_state.messages.append({"role": "user", "content": prompt})
                with st.chat_message("user"):
                    st.markdown(prompt)

                # 生成回复
                response = ""
                with st.chat_message("assistant"):
                    message_placeholder = st.empty()
                    streamer = generate_response(prompt, config, mode)
                    for text in streamer:
                        response += text
                        message_placeholder.markdown(response + "▌")
                    message_placeholder.markdown(response)
                st.session_state.messages.append({"role": "assistant", "content": response})
                store_chat_history(conn, prompt, response)

    elif assistant_type == "旅游文案助手":
        st.header("旅游文案助手")
        st.markdown("为您的旅行瞬间创作独特的文案，让每一张照片都讲述一个精彩的故事！")

        # 创建文件上传UI，并加上提示文字
        uploaded_file = st.file_uploader("上传一张图片（JPG, JPEG, PNG 格式）", type=["jpg", "jpeg", "png"])
        style_options = ["朋友圈", "小红书", "微博", "抖音"]
        style_dropdown = st.selectbox("选择您想要的风格", style_options)

        # 添加生成按钮
        generate_button = st.button("生成文案")

        # 页面布局优化
        col1, col2 = st.columns([1, 2])
        save_dir = config.save_dir
        filename = config.filename
        with col1:
            if uploaded_file is not None:
                # 保存文件
                file_path = os.path.join(save_dir, filename)
                with open(file_path, "wb") as f:
                    f.write(uploaded_file.getbuffer())

                # 显示上传成功的图片
                st.image(file_path, caption="上传的图片", use_container_width=True)

        with col2:
            if generate_button:
                if uploaded_file is not None:
                    st.success("图片已加载，正在生成文案...")

                    # 生成文案
                    generated_text = generate_text_from_image(file_path, style_dropdown, config, mode)

                    # 显示生成的文案
                    response = ""
                    placeholder = st.empty()  # 创建一个占位符
                    for i, chunk_text in enumerate(generated_text):
                        response += chunk_text
                        with placeholder.container():
                            st.text_area("生成的文案", value=response, height=200, key=f"response_text_{i}")

                    placeholder.empty()
                    st.markdown(response)

                else:
                    st.warning("请上传一张图像。")
                    # elif assistant_type == "旅游文案助手":
    #     st.header("旅游文案助手")
    #     # 模型加载

    #     # image_model_path = './models/LLM-Research/Phi-3-vision-128k-instruct'
    #     # text_model_path = "./models/shiqiyio/Qwen2-7B-Instruct_int4"

    #     save_dir = config.save_dir
    #     if not os.path.exists(save_dir):
    #         os.makedirs(save_dir)

    #     # 设置保存文件的特定名称
    #     filename = config.filename

    #     # 创建文件上传的UI
    #     uploaded_file = st.file_uploader("上传一张图片", type=["jpg", "jpeg", "png"])
    #     style_options = ["朋友圈", "小红书", "微博", "抖音"]
    #     # uploaded_file = st.file_uploader("上传图像", type=["png", "jpg", "jpeg"])
    #     style_dropdown = st.selectbox("选择您想要的风格", style_options)
    #     generate_button = st.button("生成文案")

    #     if generate_button:
    #         if uploaded_file is not None:
    #             # 设置保存路径（使用特定的文件名）
    #             file_path = os.path.join(save_dir, filename)

    #             # 将文件保存到指定目录，复写文件
    #             with open(file_path, "wb") as f:
    #                 f.write(uploaded_file.getbuffer())

    #             # 显示成功信息
    #             st.success(f"图片已加载，请稍后...")
    #             if os.path.exists(file_path):
    #                 # 图片保存成功后，展示图片
    #                 st.image(file_path)

    #                 # 生成文案
    #                 generated_text = generate_text_from_image(file_path, style_dropdown,config)

    #                 response = ""
    #                 placeholder = st.empty()  # 创建一个占位符
    #                 for i, chunk_text in enumerate(generated_text):
    #                     response += chunk_text
    #                     # 逐步更新文本框内容，提供唯一的 key
    #                     with placeholder.container():
    #                         st.text_area("生成的文案", value=response, height=400, key=f"response_text_{i}")
    #                 placeholder.empty()  # 这会清空占位符区域，移除文本框
    #                 st.markdown(response)

    #             else:
    #                 st.error("保存图像时出错。")
    #         else:
    #             st.warning("请上传一张图像。")
    # 清理消息历史
    with st.sidebar:

        clear_button = st.button("清除聊天历史")
        if clear_button:
            st.session_state.messages = []
            # st.session_state.messages = [{"role": "assistant", "content": "你好，我是你的助手，有什么我可以帮助你的吗？"}]
            st.rerun()
    # st.sidebar.button('清除模型', on_click=release_chat_model(config))


if __name__ == "__main__":
    main()
